/**
******************************************************************************
* @file    tcp_echoclient.c
* @author  MCD Application Team
* @version V1.1.0
* @date    31-July-2013
* @brief   tcp echoclient application using LwIP RAW API
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT 2013 STMicroelectronics</center></h2>
*
* Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
*        http://www.st.com/software_license_agreement_liberty_v2
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/tcp.h"
#include "memp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "netconf.h"
#include "Ethernet.h"
#include "tcp_echoclient.h"
#include "NetProtocol.h"
#include "malloc.h"
#include "ServerProtocol.h"
#if LWIP_TCP
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

vu32 message_count = 0;

struct tcp_pcb *echoclient_pcb;
struct tcp_pcb *linkpcb;

/* ECHO protocol states */
enum echoclient_states
{
    ES_NOT_CONNECTED = 0,
    ES_CONNECTED,
    ES_RECEIVED,
    ES_CLOSING,
};


/* structure to be passed as argument to the tcp callbacks */
struct echoclient
{
    enum echoclient_states state; /* connection status */
    struct tcp_pcb *pcb;          /* pointer on the current tcp_pcb */
    struct pbuf *p_tx;            /* pointer on pbuf to be transmitted */
};

struct echoclient *echoclient_es;

/* Private function prototypes -----------------------------------------------*/
static err_t tcp_echoclient_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
static void tcp_echoclient_connection_close(struct tcp_pcb *tpcb, struct echoclient *es);
static err_t tcp_echoclient_poll(void *arg, struct tcp_pcb *tpcb);
static err_t tcp_echoclient_sent(void *arg, struct tcp_pcb *tpcb, u16 len);
static void tcp_echoclient_send(struct tcp_pcb *tpcb, struct echoclient *es);
static err_t tcp_echoclient_connected(void *arg, struct tcp_pcb *tpcb, err_t err);

/* Private functions ---------------------------------------------------------*/

/***********************************************
** Function name:
** Descriptions:        TCP CLIENT 发送数据
** input parameters:    无
** output parameters:   无
** Returned value:      无
*************************************************/
void ETH_SendData(volatile char *data, u32 len)
{
    struct echoclient *es = NULL;
    /* allocate structure es to maintain tcp connection informations */
    es = (struct echoclient *)mymalloc(sizeof(struct echoclient));
    //    if(es == NULL)
    //    {
    //        return;
    //    }
#ifdef DEBUG
    {
        vu32 a;
        printf("Client send: %d\r\n", len);

        for(a = 0; a < len; a++)
        {
            printf("%02x ", data[a]);
        }

        printf("\r\n");
    }
#endif

    if(es != NULL)
    {
        es->state = ES_CONNECTED;
        es->pcb = echoclient_pcb;
        es->p_tx = NULL;
        /* allocate pbuf */
        es->p_tx = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_POOL);

        if(es->p_tx)
        {
            pbuf_take(es->p_tx, (char *)data, len);
            tcp_echoclient_send(echoclient_pcb, es);
        }
    }

    pbuf_free(es->p_tx);
    myfree(es);
}

/**
* @brief  Connects to the TCP echo server
* @param  None
* @retval None
*/
u8 tcp_echoclient_connect(lwip_addr tcp_addr, u16 tcp_port)
{
    volatile lwip_addr DestIPaddr;
    static vu16 port = 4477;

    if(echoclient_pcb != NULL)
    {
        tcp_echoclient_connection_close(echoclient_pcb, echoclient_es);
    }

    echoclient_pcb = tcp_new();

    if(echoclient_pcb != NULL)
    {
        while(tcp_bind(echoclient_pcb, IP_ADDR_ANY, port) != ERR_OK)
        {
            if(port >= 9999)
            {
                port = 3333;
            }
            else
            {
                port++;
            }
        }

        IP4_ADDR(&DestIPaddr, (u8)(tcp_addr.addr), (u8)(tcp_addr.addr >> 8), \
                 (u8)(tcp_addr.addr >> 16), (u8)(tcp_addr.addr >> 24));
        /* connect to destination address/port */
        tcp_connect(echoclient_pcb, (lwip_addr *)&DestIPaddr, tcp_port, tcp_echoclient_connected);
#ifdef DEBUG
        printf("\n\r link ok \r\n");
#endif
        return 1;
    }
    else
    {
        /* deallocate the pcb */
        memp_free(MEMP_TCP_PCB, echoclient_pcb);
#ifdef DEBUG
        printf("\n\r can not create tcp pcb");
#endif
        return 0;
    }
}

/**
* @brief  Disconnects to the TCP echo server
* @param  None
* @retval None
*/
void tcp_echoclient_disconnect(void)
{
    /* close connection */
    tcp_echoclient_connection_close(echoclient_pcb, echoclient_es);
#ifdef DEBUG
    printf("\n\r close TCP connection");
#endif
}

/**
* @brief Function called when TCP connection established
* @param tpcb: pointer on the connection contol block
* @param err: when connection correctly established err should be ERR_OK
* @retval err_t: returned error
*/
static err_t tcp_echoclient_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
    struct echoclient *es = NULL;
    vu32 len = 0;

    if(err == ERR_OK)
    {
        /* allocate structure es to maintain tcp connection informations */
        es = (struct echoclient *)mymalloc(sizeof(struct echoclient));
        //        if(es == NULL)
        //        {
        //            return 0;
        //        }
        echoclient_es = es;

        if(es != NULL)
        {
            es->state = ES_CONNECTED;
            es->pcb = tpcb;
            es->p_tx = NULL;
            /* allocate pbuf */
            es->p_tx = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_POOL);

            if(es->p_tx)
            {
                /* copy data to pbuf */
                //pbuf_take(es->p_tx, (char *)data, len);
                /* pass newly allocated es structure as argument to tpcb */
                tcp_arg(tpcb, es);
                /* initialize LwIP tcp_recv callback function */
                tcp_recv(tpcb, tcp_echoclient_recv);
                /* initialize LwIP tcp_sent callback function */
                tcp_sent(tpcb, tcp_echoclient_sent);
                /* initialize LwIP tcp_poll callback function */
                tcp_poll(tpcb, tcp_echoclient_poll, 1);
                /* send data */
                tcp_echoclient_send(tpcb, es);
                return ERR_OK;
            }
        }
        else
        {
            /* close connection */
            tcp_echoclient_connection_close(tpcb, es);
            /* return memory allocation error */
            return ERR_MEM;
        }
    }
    else
    {
        /* close connection */
        tcp_echoclient_connection_close(tpcb, es);
    }

    return err;
}
/*
* @brief tcp_receiv callback
* @param arg: argument to be passed to receive callback
* @param tpcb: tcp connection control block
* @param err: receive error code
* @retval err_t: retuned error
*/
static err_t tcp_echoclient_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    char *recdata;
    struct echoclient *es;
    err_t ret_err;
#ifdef DEBUG
    LWIP_ASSERT("arg != NULL", arg != NULL);
#endif
    es = (struct echoclient *)arg;

    /* if we receive an empty tcp frame from server => close connection */
    if(p == NULL)
    {
        /* remote host closed connection */
        es->state = ES_CLOSING;

        if(es->p_tx == NULL)
        {
            /* we're done sending, close connection */
            tcp_echoclient_connection_close(tpcb, es);
        }
        else
        {
            /* send remaining data*/
            tcp_echoclient_send(tpcb, es);
        }

        ret_err = ERR_OK;
    }
    /* else : a non empty frame was received from echo server but for some reason err != ERR_OK */
    else if(err != ERR_OK)
    {
        /* free received pbuf*/
        pbuf_free(p);
        ret_err = err;
    }
    else if(es->state == ES_CONNECTED)
    {
        /* increment message count */
        message_count++;
        /* Acknowledge data reception */
        tcp_recved(tpcb, p->tot_len);
        recdata = mymalloc(p->len);

        //        if(recdata == NULL)
        //        {
        //            return 0;
        //        }

        if(recdata != NULL)
        {
            memcpy(recdata, p->payload, p->len);
#ifdef DEBUG
            {
                vu32 a;
                printf("Client Rec: %d\r\n", p->len);

                for(a = 0; a < p->len; a++)
                {
                    printf("%02x ", recdata[a]);
                }

                printf("\r\n");
            }
#endif

            //数据是HM开头  服务器数据
            if((recdata[0] == 0x48) && (recdata[1] == 0x4d))
            {
                //处理接受的服务器数据
                pb_receive(recdata);
            }
            //数据是A5开头  服务器数据
            else if(recdata[0] == 0xA5)
            {
                Process_Server_Data(recdata);
            }
        }

        myfree(recdata);
        /* free received pbuf*/
        pbuf_free(p);
        ret_err = ERR_OK;
    }
    /* data received when connection already closed */
    else
    {
        /* Acknowledge data reception */
        tcp_recved(tpcb, p->tot_len);
        /* free pbuf and do nothing */
        pbuf_free(p);
        ret_err = ERR_OK;
    }

    return ret_err;
}

/**
* @brief function used to send data
* @param  tpcb: tcp control block
* @param  es: pointer on structure of type echoclient containing info on data
*             to be sent
* @retval None
*/
static void tcp_echoclient_send(struct tcp_pcb *tpcb, struct echoclient *es)
{
    struct pbuf *ptr;
    err_t wr_err = ERR_OK;

    while((wr_err == ERR_OK) &&
            (es->p_tx != NULL) &&
            (es->p_tx->len <= tcp_sndbuf(tpcb)))
    {
        /* get pointer on pbuf from es structure */
        ptr = es->p_tx;
        /* enqueue data for transmission */
        wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);

        if(wr_err == ERR_OK)
        {
            /* continue with next pbuf in chain (if any) */
            es->p_tx = ptr->next;

            if(es->p_tx != NULL)
            {
                /* increment reference count for es->p */
                pbuf_ref(es->p_tx);
            }

            /* free pbuf: will free pbufs up to es->p (because es->p has a reference count > 0) */
            pbuf_free(ptr);
        }
        else if(wr_err == ERR_MEM)
        {
            /* we are low on memory, try later, defer to poll */
            es->p_tx = ptr;
        }
        else
        {
            /* other problem ?? */
        }
    }
}

/**
* @brief  This function implements the tcp_poll callback function
* @param  arg: pointer on argument passed to callback
* @param  tpcb: tcp connection control block
* @retval err_t: error code
*/
static err_t tcp_echoclient_poll(void *arg, struct tcp_pcb *tpcb)
{
    err_t ret_err;
    struct echoclient *es;
    es = (struct echoclient *)arg;

    if(es != NULL)
    {
        if(es->p_tx != NULL)
        {
            /* there is a remaining pbuf (chain) , try to send data */
            tcp_echoclient_send(tpcb, es);
        }
        else
        {
            /* no remaining pbuf (chain)  */
            if(es->state == ES_CLOSING)
            {
                /* close tcp connection */
                tcp_echoclient_connection_close(tpcb, es);
            }
        }

        ret_err = ERR_OK;
    }
    else
    {
        /* nothing to be done */
        tcp_abort(tpcb);
        ret_err = ERR_ABRT;
    }

    return ret_err;
}

/**
* @brief  This function implements the tcp_sent LwIP callback (called when ACK
*         is received from remote host for sent data)
* @param  arg: pointer on argument passed to callback
* @param  tcp_pcb: tcp connection control block
* @param  len: length of data sent
* @retval err_t: returned error code
*/
static err_t tcp_echoclient_sent(void *arg, struct tcp_pcb *tpcb, u16 len)
{
    struct echoclient *es;
    LWIP_UNUSED_ARG(len);
    es = (struct echoclient *)arg;

    if(es->p_tx != NULL)
    {
        /* still got pbufs to send */
        tcp_echoclient_send(tpcb, es);
    }

    return ERR_OK;
}

/**
* @brief This function is used to close the tcp connection with server
* @param tpcb: tcp connection control block
* @param es: pointer on echoclient structure
* @retval None
*/
static void tcp_echoclient_connection_close(struct tcp_pcb *tpcb, struct echoclient *es)
{
    /* remove callbacks */
    tcp_abort(tpcb);
    tcp_arg(tpcb, NULL);
    tcp_recv(tpcb, NULL);
    tcp_sent(tpcb, NULL);
    tcp_err(tpcb, NULL);
    tcp_poll(tpcb, NULL, 0);

    if(es != NULL)
    {
        myfree(es);
    }

    /* close tcp connection */
    tcp_close(tpcb);
}

#endif /* LWIP_TCP */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
